package com.quanyan.ic.protocol.server.handler;

import com.quanyan.ic.cache.PlaceLightSettingDao;
import com.quanyan.ic.protocol.Code;
import com.quanyan.ic.protocol.MessageType;
import com.quanyan.ic.protocol.codec.protobuf.ICMessageBuilder;
import com.quanyan.ic.protocol.codec.protobuf.ICMessageProto;
import com.quanyan.ic.protocol.server.LightServer;
import com.quanyan.ic.protocol.server.SendICMessageProcessor;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * Created by zhuo.shi on 2017/6/1.
 */
@Component
public class HandShakeRespHandler extends ChannelInboundHandlerAdapter {

    private static Logger logger = LoggerFactory.getLogger(HandShakeRespHandler.class);

    @Autowired
    SendICMessageProcessor sendICMessageProcessor;

    @Autowired
    private PlaceLightSettingDao placeLightSettingDao;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        ICMessageProto.ICMessage icMessage = (ICMessageProto.ICMessage) msg;
        if (icMessage.getHeader() != null && icMessage.getHeader().getType() == MessageType.HANDSHAKE_REQ.value()) {
            //如果握手消息成功
            ICMessageProto.ICMessage.Header header = icMessage.getHeader();
            logger.info("server received handshake msg: {} from client [{}]", icMessage, ctx.channel().remoteAddress());
            if (header.hasPid()) {
                logger.info("handshake success of client [{}]", ctx.channel().remoteAddress());

                //保存客户端和服务端直接的通道
                LightServer.getSessionChannelMap().put(String.valueOf(header.getPid()), ctx.channel());
                //placeLightSettingDao.saveSessionChannel(header.getPid(),ctx.channel());

                //检查失败消息集合中是否有此客户端的消息，如果有，则重新下发（异步）
//                ctx.executor().submit(
//                        new HandShakeRespHandler.FailedMsgResendTask(header.getPid(), ctx.channel()));
//                new Thread(new HandShakeRespHandler.FailedMsgResendTask(header.getPid(), ctx.channel())).start();

                //握手成功
                ctx.writeAndFlush(ICMessageBuilder.buildHandShakeMsg(header.getPid(), Code.RC_200));
            } else {
                logger.info("handshake failed of client [{}]", ctx.channel().remoteAddress());
                ctx.writeAndFlush(ICMessageBuilder.buildHandShakeMsg(header.getPid(), Code.RC_201)); //握手认证失败
            }
        } else {
            ctx.fireChannelRead(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.warn("client {} disconnect, cause by {}", ctx.channel().remoteAddress(), cause.getMessage());

        //判断是否是RST包的导致的异常
        //因为4层TCP来源IP为客户端的IP，所以若是10、100开头的IP那么肯定是SLB的健康检查IP，这里为了节约篇幅直接判断是否是10或100开头，实际应用中可以详尽的根据上文中的健康检查IP来判断
        if (cause.getMessage().startsWith("Connection reset")
                && (ctx.channel().remoteAddress().toString().startsWith("/10.")
                || ctx.channel().remoteAddress().toString().startsWith("/100.") || ctx.channel().remoteAddress().toString().startsWith("/42.120.165.21"))) {
            logger.info("health check!");
            return;
        }

        //移除此客户端session
        Collection<Channel> col = LightServer.getSessionChannelMap().values();
        col.remove(ctx.channel());
        logger.info("remove session, client={}", ctx.channel().remoteAddress());
        //关闭
        ctx.close();
        ctx.fireExceptionCaught(cause);
    }

//    private class FailedMsgResendTask implements Runnable{
//
//        private int pid;
//        private Channel channel;
//
//        protected FailedMsgResendTask(int pid, Channel channel){
//            this.pid = pid;
//            this.channel = channel;
//        }
//
//        @Override
//        public void run() {
//            Set<String> list = redisTemplate.opsForSet().members(RedisKey.buildSendFailedOrderMsgKey());
//            for (String json : list){
//                OrderMessage orderMsg = JSON.parseObject(json, OrderMessage.class);
//                if (orderMsg.getPlaceId() == this.pid){
//                    logger.info("find the failed orderMsg {}, try to resend ", json);
//                    sendICMessageProcessor.sendICMessage(orderMsg, channel);
//                }
//            }
//        }
//    }
}
